package com.dabai.iot.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dabai.common.core.exception.ServiceException;
import com.dabai.common.core.utils.CollectionUtils;
import com.dabai.common.core.utils.Func;
import com.dabai.common.core.utils.ObjectUtils;
import com.dabai.common.redis.utils.RedisUtils;
import com.dabai.common.tenant.helper.TenantHelper;
import com.dabai.iot.core.cache.CacheKey;
import com.dabai.iot.core.convert.AlarmConvert;
import com.dabai.iot.core.dto.AlarmConditionInput;
import com.dabai.iot.core.dto.AlarmInput;
import com.dabai.iot.core.dto.AlarmListenerInput;
import com.dabai.iot.core.dto.AlarmQuery;
import com.dabai.iot.core.entity.*;
import com.dabai.iot.core.mapper.AlarmMapper;
import com.dabai.iot.core.service.*;
import com.dabai.iot.core.vo.AlarmListenerVO;
import com.dabai.iot.core.vo.AlarmVO;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * 告警配置 Service 实现类
 *
 * @author kev1n
 */
@Service
@RequiredArgsConstructor
public class AlarmServiceImpl extends ServiceImpl<AlarmMapper, AlarmEntity> implements IAlarmService {

    private final IProductService productService;
    private final IAlarmDeviceRelationService alarmDeviceRelationService;
    private final IAlarmListenerService alarmListenerService;
    private final IAlarmActionService alarmActionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addAlarm(AlarmInput alarmInput) {
        if (checkAlarmNameDuplicate(null, alarmInput.getAlarmName())) {
            throw new ServiceException("已经存在相同的配置名称");
        }

        AlarmEntity alarmEntity = new AlarmEntity();
        alarmEntity.setAlarmName(alarmInput.getAlarmName());
        alarmEntity.setAlarmDesc(alarmInput.getAlarmDesc());
        alarmEntity.setProductId(alarmInput.getProductId());
        alarmEntity.setLevel(alarmInput.getLevel());
        alarmEntity.setEnabled(alarmInput.getEnabled());
        boolean save = this.save(alarmEntity);
        if (save) {
            Long alarmId = alarmEntity.getId();
            // 绑定告警配置与设备
            assignAlarmDeviceRelation(alarmId, alarmInput.getDeviceIds());
            // 新增监听器
            addAlarmListener(alarmId, alarmInput.getAlarmListener());
            // 新增输出
            addAlarmActions(alarmId, alarmInput.getAlarmActions());
        }

        return save;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAlarm(AlarmInput alarmInput) {
        AlarmEntity alarmEntity = this.getById(alarmInput.getId());
        if (alarmEntity == null) {
            throw new ServiceException("告警配置不存在");
        }

        if (checkAlarmNameDuplicate(alarmInput.getId(), alarmInput.getAlarmName())) {
            throw new ServiceException("已经存在相同的配置名称");
        }

        alarmEntity.setAlarmName(alarmInput.getAlarmName());
        alarmEntity.setAlarmDesc(alarmInput.getAlarmDesc());
        alarmEntity.setLevel(alarmInput.getLevel());
        alarmEntity.setEnabled(alarmInput.getEnabled());
        boolean update = this.updateById(alarmEntity);
        if (update) {
            Long alarmId = alarmEntity.getId();
            // 绑定告警配置与设备
            assignAlarmDeviceRelation(alarmId, alarmInput.getDeviceIds());

            // 删除旧的监听器
            alarmListenerService.removeAlarmListener(alarmId);
            // 添加新的监听器
            addAlarmListener(alarmId, alarmInput.getAlarmListener());

            // 删除旧的输出
            alarmActionService.removeAlarmActions(alarmId);
            // 添加新的输出
            addAlarmActions(alarmId, alarmInput.getAlarmActions());
        }

        return update;
    }

    /**
     * 校验配置名称是否重复(企业内唯一)
     */
    boolean checkAlarmNameDuplicate(Long id, String alarmName) {
        return this.lambdaQuery().
            eq(AlarmEntity::getAlarmName, alarmName)
            .ne(ObjectUtil.isNotNull(id), AlarmEntity::getId, id)
            .exists();
    }

    /**
     * 绑定告警配置与设备
     */
    private void assignAlarmDeviceRelation(Long alarmId, List<Long> inputDeviceIds) {
        List<AlarmDeviceRelationEntity> list = alarmDeviceRelationService.lambdaQuery()
            .eq(AlarmDeviceRelationEntity::getAlarmId, alarmId)
            .list();

        // 获取原来绑定的设备
        Set<Long> dbDeviceIds = CollectionUtils.convertSet(list, AlarmDeviceRelationEntity::getDeviceId);

        // 计算需要新增和删除的绑定
        Collection<Long> createDeviceIds = CollUtil.subtract(inputDeviceIds, dbDeviceIds);
        Collection<Long> deleteDeviceIds = CollUtil.subtract(dbDeviceIds, inputDeviceIds);
        // 执行新增和删除操作
        if (CollUtil.isNotEmpty(createDeviceIds)) {
            alarmDeviceRelationService.saveBatch(CollectionUtils.convertList(createDeviceIds, deviceId -> {
                AlarmDeviceRelationEntity entity = new AlarmDeviceRelationEntity();
                entity.setAlarmId(alarmId);
                entity.setDeviceId(deviceId);
                return entity;
            }));
        }
        if (CollUtil.isNotEmpty(deleteDeviceIds)) {
            alarmDeviceRelationService.remove(Wrappers.<AlarmDeviceRelationEntity>lambdaQuery()
                .eq(AlarmDeviceRelationEntity::getAlarmId, alarmId)
                .in(AlarmDeviceRelationEntity::getDeviceId, deleteDeviceIds));

            // 循环删除与设备关联的告警配置缓存
            // TODO: 这里会删除所有与该设备关联的告警配置缓存，后面可以优化下
            for (Long deviceId : deleteDeviceIds) {
                deleteDeviceRelatedAlarmList(deviceId);
            }
        }
    }

    /**
     * 添加新的输出
     */
    private void addAlarmActions(Long alarmId, List<AlarmActionEntity> alarmActions) {
        if (alarmActions == null) {
            return;
        }
        for (AlarmActionEntity alarmAction : alarmActions) {
            alarmAction.setAlarmId(alarmId);
        }

        alarmActionService.saveBatch(alarmActions);
    }

    /**
     * 新增监听器
     */
    private void addAlarmListener(Long alarmId, AlarmListenerInput alarmListenerInput) {
        // 获取所有条件
        List<AlarmConditionInput> conditions = alarmListenerInput.getConditions();

        AlarmListenerEntity alarmListener = new AlarmListenerEntity();
        alarmListener.setAlarmId(alarmId);
        alarmListener.setConditions(JSONArray.from(conditions).toJSONString());
        alarmListenerService.save(alarmListener);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeAlarm(Long alarmId) {
        // 获取与该告警配置关联的绑定关系
        List<AlarmDeviceRelationEntity> relationList = alarmDeviceRelationService.getRelationListByAlarmId(alarmId);
        for (AlarmDeviceRelationEntity relation : relationList) {
            // 循环删除与设备关联的告警配置缓存
            // TODO: 这里会删除所有与该设备关联的告警配置缓存，后面可以优化下
            deleteDeviceRelatedAlarmList(relation.getDeviceId());
        }

        // 删除绑定关系
        alarmDeviceRelationService.removeRelationListByAlarmId(alarmId);
        // 删除监听器
        alarmListenerService.removeAlarmListener(alarmId);
        // 删除输出
        alarmActionService.removeAlarmActions(alarmId);

        return removeById(alarmId);
    }

    @Override
    public AlarmVO getAlarmInfo(Long alarmId) {
        AlarmEntity alarmEntity = getById(alarmId);
        if (alarmEntity == null) {
            return null;
        }

        AlarmVO alarmVO = AlarmConvert.INSTANCE.convertVO(alarmEntity);

        // 获取关联的产品名称
        ProductEntity product = productService.getProductCacheById(alarmEntity.getProductId());
        alarmVO.setProductName(product.getProductName());

        // 获取关联的设备id
        List<Long> deviceIds = alarmDeviceRelationService.lambdaQuery()
            .eq(AlarmDeviceRelationEntity::getAlarmId, alarmId)
            .list()
            .stream()
            .map(AlarmDeviceRelationEntity::getDeviceId)
            .toList();
        alarmVO.setDeviceIds(deviceIds);

        // 查询监听器
        AlarmListenerEntity alarmListener = alarmListenerService.lambdaQuery()
            .eq(AlarmListenerEntity::getAlarmId, alarmId)
            .one();
        String jsonString = alarmListener.getConditions();
        List<AlarmConditionInput> conditions = JSON.parseArray(jsonString, AlarmConditionInput.class);

        AlarmListenerVO alarmListenerVO = new AlarmListenerVO();
        alarmListenerVO.setConditions(conditions);
        alarmVO.setAlarmListener(alarmListenerVO);

        // 查询输出
        List<AlarmActionEntity> alarmActions = alarmActionService.getAlarmActionsByAlarmId(alarmId);
        alarmVO.setAlarmActions(alarmActions);

        return alarmVO;
    }

    @Override
    public IPage<AlarmEntity> getAlarmPage(IPage<AlarmEntity> page, AlarmQuery alarmQuery) {
        QueryWrapper<AlarmEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
            .eq(Func.isNotEmpty(alarmQuery.getId()), AlarmEntity::getId, alarmQuery.getId())
            .eq(Func.isNotEmpty(alarmQuery.getTenantId()), AlarmEntity::getTenantId, alarmQuery.getTenantId())
            .like(Func.isNotEmpty(alarmQuery.getAlarmName()), AlarmEntity::getAlarmName, alarmQuery.getAlarmName())
            .orderByDesc(AlarmEntity::getCreateTime);

        return page(page, queryWrapper);
    }


    @Override
    public List<AlarmEntity> getAlarmListByRelationDevice(Long deviceId) {
        MPJLambdaWrapper<AlarmEntity> wrapper = JoinWrappers.lambda(AlarmEntity.class)
            .selectAll(AlarmEntity.class)
            .innerJoin(AlarmDeviceRelationEntity.class, on -> on
                .eq(AlarmDeviceRelationEntity::getAlarmId, AlarmEntity::getId)
                .eq(AlarmDeviceRelationEntity::getDeviceId, deviceId)
            );

        return baseMapper.selectJoinList(wrapper);
    }

    @Override
    public List<AlarmEntity> getAlarmListCacheByRelationDevice(Long deviceId) {
        String cacheKey = CacheKey.getAlarmRelationKey(deviceId);
        List<AlarmEntity> alarmListCache = TenantHelper.ignore(() -> RedisUtils.getCacheList(cacheKey));
        if (ObjectUtils.isNotEmpty(alarmListCache)) {
            return alarmListCache;
        }

        List<AlarmEntity> relationList = this.getAlarmListByRelationDevice(deviceId);
        if (ObjectUtils.isNotEmpty(relationList)) {
            alarmListCache = relationList;
            TenantHelper.ignore(() -> RedisUtils.setCacheList(cacheKey, relationList));
        }
        return alarmListCache;
    }

    @Override
    public void deleteDeviceRelatedAlarmList(Long deviceId) {
        String cacheKey = CacheKey.getAlarmRelationKey(deviceId);
        TenantHelper.ignore(() -> RedisUtils.deleteObject(cacheKey));
    }

}
